import { apiErrorToast, getResponseErrorMessage } from './response-error'

const TOKEN_KEY = 'jwt_token'

export function clearToken() {
  localStorage.removeItem(TOKEN_KEY)
}
export function getToken() {
  return localStorage.getItem(TOKEN_KEY)
}
export function setToken(token: string) {
  localStorage.setItem(TOKEN_KEY, token)
}

export class RequestError extends Error {
  data: unknown
  constructor(message: string, data: unknown) {
    super(message)
    this.data = data
    this.name = 'RequestError'
  }
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ReturnValueType<T> = T extends (...args: any[]) => infer R ? (R extends Promise<infer R2> ? R2 : R) : never

export async function request<T = unknown>(data: {
  url: string
  method: 'get' | 'post' | 'put' | 'delete'
  json: boolean // 返回值是否是json解析
  params?: Record<string, string>
  body?: Record<string, unknown> | FormData
}): Promise<T> {
  const token = getToken()
  const search = new URLSearchParams()
  if (data.params) {
    Object.entries(data.params).forEach(([key, value]) => {
      search.set(key, value)
    })
  }
  let res: Response
  if (data.body instanceof FormData) {
    res = await fetch(`${data.url}?${search.toString()}`, {
      method: data.method,
      body: data.body,
      headers: {
        Authorization: token ? `JWT ${token}` : '',
      },
    })
  } else {
    res = await fetch(`${data.url}?${search.toString()}`, {
      method: data.method,
      body: data.body ? JSON.stringify(data.body) : undefined,
      headers: {
        Authorization: token ? `JWT ${token}` : '',
        'Content-Type': 'application/json',
      },
    })
  }
  if (res.status === 401) {
    clearToken()
    window.location.href = '/login'
  }
  if (res.ok) {
    if (data.json) {
      return res.json()
    }
    return res as T
  }
  let errorData: unknown
  let isJSON = false
  try {
    errorData = await res.json()
    isJSON = true
  } catch {
    errorData = res.statusText
  }
  apiErrorToast(errorData)
  if (isJSON) {
    throw new RequestError(getResponseErrorMessage(errorData), errorData)
  }
  throw new RequestError(res.statusText, undefined)
}
